#include <atomic>
#include <filesystem>
#include <fstream>
#include <thread>
#include "async_logger.h"
#include "file_output_strategy.h"
#include "gtest/gtest.h"

namespace Base {
namespace SystemLog {
namespace {
constexpr const char* TEST_LOG_DIR = "out/tests/logs";
constexpr const int32_t TEST_MAX_FILE_SIZE = 10 * 1024 * 1024; // 10MB
constexpr const int32_t TEST_THREAD_COUNT = 8;
constexpr const int32_t TEST_MESSAGE_PER_THREAD = 10000;
constexpr const int32_t TEST_SUPER_MAX_FILE_SIZE = 100 * 1024 * 1024; // 100MB
constexpr const int32_t TEST_ROLL_INTERVAL = 2;


std::string ReadFileContent(const std::string &path) {
    std::ifstream file(path);
    if (!file.is_open()) {
        return "";
    }
    return std::string((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
}
} // namespace

class IntegrationTest : public testing::Test {
public:
    static void SetUpTestCase(void);
    static void TearDownTestCase(void);
    void SetUp();
    void TearDown();
};

void IntegrationTest::SetUpTestCase(void)
{}

void IntegrationTest::TearDownTestCase(void)
{}

void IntegrationTest::SetUp(void)
{
    std::filesystem::create_directories(TEST_LOG_DIR);
}

void IntegrationTest::TearDown(void)
{
    try {
        if (std::filesystem::exists(TEST_LOG_DIR)) {
            std::filesystem::remove_all(TEST_LOG_DIR);
        } else {
            GTEST_LOG_(ERROR) << "directory does not exist: " << TEST_LOG_DIR << '\n';
        }
    } catch (const std::filesystem::filesystem_error &ex) {
        GTEST_LOG_(ERROR) << "deletion failed: " << ex.what() << '\n';
    }
}

/**
 * @tc.name: IntegrationTest_001
 * @tc.desc: verify AsyncLogger with FileOutput.
 * @tc.type: FUNC
 */
TEST_F(IntegrationTest, IntegrationTest_001) {
    // 创建异步日志器

    // 添加文件输出策略
    FileOutputOptions options;
    options.filePathPattern = std::string(TEST_LOG_DIR) + "/async.log";
    AsyncLogger<>::GetInstance().AddOutputStrategy(std::make_unique<FileOutputStrategy>(options));

    // 启动日志器
    ASSERT_EQ(AsyncLogger<>::GetInstance().Start(), ERR_OK);

    // 写入日志
    const char* logMsg = "Async logging test message\n";
    ASSERT_EQ(AsyncLogger<>::GetInstance().Append(logMsg, strlen(logMsg)), ERR_OK);

    // 等待日志刷新
    std::this_thread::sleep_for(std::chrono::milliseconds(100));
    ASSERT_EQ(AsyncLogger<>::GetInstance().Stop(), ERR_OK);

    // 验证文件内容
    auto content = ReadFileContent(options.filePathPattern);
    ASSERT_TRUE(content.find(logMsg) != std::string::npos);
}

/**
 * @tc.name: IntegrationTest_002
 * @tc.desc: verify high concurrency logging.
 * @tc.type: FUNC
 */
TEST_F(IntegrationTest, IntegrationTest_002) {
    FileOutputOptions options;
    options.filePathPattern = std::string(TEST_LOG_DIR) + "/concurrent_%Y%m%d_%H%M%S.log";
    options.maxFileSize = TEST_MAX_FILE_SIZE;
    AsyncLogger<>::GetInstance().AddOutputStrategy(std::make_unique<FileOutputStrategy>(options));

    ASSERT_EQ(AsyncLogger<>::GetInstance().Start(), ERR_OK);

    std::atomic<int32_t> completed(0);
    std::atomic<size_t> totalWriteBytes(0);

    auto worker = [&] {
        for (int i = 0; i < TEST_MESSAGE_PER_THREAD; ++i) {
            std::string msg = "Thread " + std::to_string(i) + " message\n";
            totalWriteBytes.fetch_add(msg.size(), std::memory_order_release);
            AsyncLogger<>::GetInstance().Append(msg.c_str(), msg.size());
        }
        ++completed;
    };

    std::vector<std::thread> threads;
    for (int i = 0; i < TEST_THREAD_COUNT; ++i) {
        threads.emplace_back(worker);
    }

    // 等待所有线程完成
    for (auto& t : threads) {
        t.join();
    }

    ASSERT_EQ(AsyncLogger<>::GetInstance().Flush(), ERR_OK);
    ASSERT_EQ(AsyncLogger<>::GetInstance().Stop(), ERR_OK);

    // 验证总日志量
    size_t totalBytes = 0;
    for (const auto& entry : std::filesystem::directory_iterator(TEST_LOG_DIR)) {
        if (entry.path().string().find("concurrent") != std::string::npos) {
            totalBytes += std::filesystem::file_size(entry.path());
        }
    }

    ASSERT_EQ(totalBytes, totalWriteBytes);
}

/**
 * @tc.name: IntegrationTest_003
 * @tc.desc: verify file rolling by time.
 * @tc.type: FUNC
 */
TEST_F(IntegrationTest, IntegrationTest_003) {
    FileOutputOptions options;
    options.filePathPattern = std::string(TEST_LOG_DIR) + "/time_roll_%Y%m%d_%H%M%S.log";
    options.maxFileSize = TEST_SUPER_MAX_FILE_SIZE;
    options.rollInterval = std::chrono::seconds(TEST_ROLL_INTERVAL); // 每2秒滚动

    AsyncLogger<>::GetInstance().AddOutputStrategy(std::make_unique<FileOutputStrategy>(options));
    ASSERT_EQ(AsyncLogger<>::GetInstance().Start(), ERR_OK);

    // 写入初始日志
    AsyncLogger<>::GetInstance().Append("First log entry\n", 16);

    // 等待第一次滚动
    std::this_thread::sleep_for(std::chrono::seconds(3));

    // 写入第二次日志
    AsyncLogger<>::GetInstance().Append("Second log entry\n", 17);

    // 等待第二次滚动
    std::this_thread::sleep_for(std::chrono::seconds(3));
    ASSERT_EQ(AsyncLogger<>::GetInstance().Stop(), ERR_OK);

    // 验证文件数量
    int fileCount = 0;
    for (const auto& entry : std::filesystem::directory_iterator(TEST_LOG_DIR)) {
        if (entry.path().string().find("time_roll") != std::string::npos) {
            ++fileCount;
        }
    }
    ASSERT_EQ(fileCount, 2); // 至少有两个文件
}
} // namespace SystemLog
} // namespace Base
